package vip.xiaomoli.lock.component;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Objects;

@Component
public class DefaultLockKeyGenerator implements LockKeyGenerator {

    private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();

    private static final ExpressionParser PARSER = new SpelExpressionParser();

    @Override
    public String generateKey(ProceedingJoinPoint joinPoint, String[] bizKeys) {
        StringBuilder sb = new StringBuilder(KEY_PREFIX);
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        sb.append(methodSignature.getDeclaringTypeName())
                .append(".").append(methodSignature.getName()).append("(");
        Class[] paramTypeArr = methodSignature.getParameterTypes();
        for (int i=0; i<paramTypeArr.length; i++) {
            sb.append(paramTypeArr[i].getSimpleName());
            if (i < paramTypeArr.length-1) sb.append(",");
        }
        sb.append(")").append(getBizKey(bizKeys, methodSignature.getMethod(), joinPoint.getArgs()));
        return sb.toString();
    }

    private String getBizKey(String[] bizKeys, Method method, Object[] parameterValues) {
        StringBuilder sb = new StringBuilder();
        if (bizKeys.length > 0) {
            sb.append("#");
            EvaluationContext context = new MethodBasedEvaluationContext(null, method, parameterValues, NAME_DISCOVERER);
            for (int i=0; i<bizKeys.length; i++) {
                String bizKey = bizKeys[i];
                if (!StringUtils.isEmpty(bizKey)) {
                    String value = Objects.requireNonNull(PARSER.parseExpression(bizKey).getValue(context)).toString();
                    sb.append(value);
                    if (i < bizKeys.length-1) sb.append("_");
                }
            }
        }
        return sb.toString();
    }

}
